#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "hashTable.h" 




unsigned jenkins_one_at_a_time_hash(char *key)
{
    unsigned hash = 0;

    for (; *key; ++key) 
        {
        hash += *key;
        hash += (hash << 10);
        hash ^= (hash >> 6);
    }
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    return hash;
}

void ht_init(HashTable *ht, size_t size, HashFunction hashfunc, Destructor dtor)
{
        size_t i;
        ht        =  (HashTable*) malloc (sizeof (HashTable));
        ht->table =  (List**) malloc (sizeof(List)*size);
        ht->size  =  size;
        if(hashfunc != NULL){
			ht->hashfunc = hashfunc;
        }
        else{
            hashfunc = jenkins_one_at_a_time_hash;
        }

        if(dtor != NULL){
            ht->dtor = dtor;
        }
        else{
           ht->dtor = NULL;
        }

        for(i = 0; i < ht->size; i ++){
           ht->table[i] = NULL;
        }
}

void ht_destroy(HashTable *ht)
{
        size_t i;
        if(ht->dtor == NULL){
			for(i = 0; i < ht->size; i ++){
				free(ht->table[i]);
            }
        }
        else{
			for(i; i < ht->size; i ++){
				ht->dtor(ht->table[i]->data);
                        free(ht->table);
           }
        }
        free(ht->table);
        free(ht);
}

void ht_set(HashTable *ht, char *key, Pointer data)
{
        unsigned index;
        List * buffer;
        index = ht->hashfunc(key) % ht->size;
        buffer   = ht->table[index];

        if(ht->dtor == NULL){
            while(buffer != NULL){
				if(strcmp(buffer->key, key) == 0){
					buffer->data = data;
                    return;
                }
                buffer = buffer->next;
            }
        }
        else{
			while(buffer != NULL){
				if(strcmp(buffer->key, key) == 0){
					ht->dtor(buffer->data);
                    buffer->data = data;
                    return;
                }
                        buffer = buffer->next;
                }
        }
        ht->table[index] = slist_prepend(ht->table[index], data, key);
}

Pointer ht_get(HashTable *ht, char *key)
{
        size_t index;
        List   *buffer;
        index  = ht->hashfunc(key) % ht->size;
        buffer = ht->table[index];

        while(buffer != NULL){
			if(strcmp(buffer->key, key) == 0){
				return buffer->data;
            }
            buffer = buffer->next;
        }
        return NULL;
}

int ht_has(HashTable *ht, char *key)
{
        size_t index;
        List * buffer;
        index  = ht->hashfunc(key) % ht->size;
        buffer = ht->table[index];
        while(buffer != NULL){
			if(strcmp(buffer->key, key) == 0){
				return 1;
	        }
            buffer = buffer->next;
        }
        return 0;
}

void ht_delete(HashTable *ht, char *key)
{
        size_t index;
        List * buffer;
        List * buf2;
        index     = ht->hashfunc(key)%ht->size;
        buffer    = ht->table[index];
        if(buffer == NULL){
                return;
        }
        while(buffer->next != NULL){
			buf2 = buffer;
            if(strcmp(buffer->key, key) == 0){
				if(ht->dtor != NULL){
					ht->dtor(buffer->data);
					buf2->next = buffer->next;
                    free(buffer);
                    return;
               }
               else{
				   buf2->next = buffer->next;
                   free(buffer);
                   return;
               }
           }
           buffer = buffer->next;
        }

        buf2 = buffer;

        if(strcmp(buffer->key, key) == 0){
			if(ht->dtor != NULL){
				ht->dtor(buffer->data);
				buf2->next = buffer->next;
                free(buffer);
                return;
            }
            else{
				buf2->next=buffer->next;
                free(buffer);
                return;
           }
        }
}

List *slist_prepend(List *list, Pointer data, char* key)
{
    List *newlist;
    newlist = (List*) malloc (sizeof(List));
    newlist->key  = key;
    newlist->data = data;
    newlist->next = list;
    return newlist;
}

void slist_foreach(List *list, void (*f)(char *key, Pointer data))
{
        while(list != NULL){
			f(list->key, list->data);
            list = list->next;
        }
}

int ht_has(HashTable *ht, char *key)
{
        size_t index;
        List * buf;
        index = ht->hashfunc(key)%ht->size;
        buf = ht->table[index];
        while(buf!=NULL)
        {
                if(strcmp(buf->key, key)==0)
                {
                        return 1;
                }
                buf=buf->next;
        }
        return 0;
}



void ht_traverse(HashTable *ht, void (*f)(char *key, Pointer data))
{
        size_t i;
        List * buf;
        for(i=0; i<ht->size; i++)
        {
                buf=ht->table[i];
                slist_foreach(buf, f);
        }
}

void ht_resize(HashTable *ht, size_t new_size)
{
        List ** new_table;
        List * buf;
        size_t i;
        size_t index;
        new_table=(List**)malloc(sizeof(List)*new_size);
        for(i=0; i<ht->size; i++)
        {
                buf=ht->table[i];
                while(buf!=0)
                {
                        index = ht->hashfunc(buf->key) % new_size;
                        slist_prepend(new_table[index], buf->data, buf->key);
                        buf=buf->next;
                }
                free(ht->table[i]);
        }
        free(ht->table);
        ht->table=new_table;
        ht->size=new_size;
}
